package com.sen.common.tags;

import java.util.LinkedHashMap;
import java.util.Map;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.TagSupport;

import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import com.sen.common.orm.PublicDao;
import com.sen.common.utils.spring.SpringUtils;
import com.sen.common.widget.Attribute;
import com.sen.common.widget.form.Hidden;
import com.sen.common.widget.form.Label;
import com.sen.common.widget.form.Select;
import com.sen.common.widget.media.JavaScript;
import com.sen.common.widget.table.TableCell;

/**
 * 连动下拉选择标签.
 * 
 * @author sen
 */
public abstract class AbstractDoubleSelectTag extends TagSupport {

	private static final long serialVersionUID = -8877756921414987766L;
	private static Logger log = Logger
			.getLogger(AbstractDoubleSelectTag.class);

	protected PublicDao publicDao;
	
	// 是否异步加载数据? 默认为: 同步.
	private String async = "false";

	private String firstLabel = "";
	private String firstId;
	private String firstName;
	private String firstNullable = "YES";
	private String firstEmptyOption = "";
	private String firstValue = "";
	private String firstStyle;
	private String firstClassName;
	private Map<String, String> firstOptions;

	private String secondLabel = "";
	private String secondId;
	private String secondName;
	private String secondNullable = "YES";
	private String secondEmptyOption = "";
	private String secondValue = "";
	private String secondStyle;
	private String secondClassName;
	private Map<String, String> secondOptions;

	private String wrappedInTd = "false";

	@Override
	public int doStartTag() throws JspException {
		try {
			initSettings();

			firstOptions = new LinkedHashMap<String, String>();
			secondOptions = new LinkedHashMap<String, String>();

			publicDao = SpringUtils.getBean(pageContext.getServletContext(),
					"publicDao", PublicDao.class);

			JspWriter out = pageContext.getOut();
			String buildDoubleSelectHtml = buildDoubleSelectHtml();
			out.print(buildDoubleSelectHtml);
			
			out.flush();
		} catch (Exception e) {
			log.warn("AbstractDoubleSelectTag doStartTag报错", e);
		}
		return SKIP_BODY;
	}

	/**
	 * 初始化默认的参数设置s
	 */
	protected void initSettings() {
		// empty!
	}

	@Override
	public int doEndTag() throws JspException {
		try {
			JspWriter out = pageContext.getOut();
			out.print(outAfterFinishedTag());
			if (isTrue(async)) { // 异步加载第一个下拉框数据
				JavaScript js = new JavaScript();
				js.add(asyncLoadFirstSelectOptionsFunction());
				out.print(js.toHtml());
			}
		} catch (Exception e) {
			log.warn("AbstractDoubleSelectTag doEndTag报错", e);
		}
		return SKIP_BODY;
	}

	/**
	 * 获取在标签完成后输出的额外信息(常用于js代码)
	 * 
	 * @return 额外信息字符串
	 */
	protected String outAfterFinishedTag() {
		// default is empty!
		return StringUtils.EMPTY;
	}

	private String buildDoubleSelectHtml() {
		return buildFirstSelectHtml() + initSecondSelectHtml();
	}

	private String buildFirstSelectHtml() {

		// 两个选择框设置默认值
		Hidden firstSelectIdHidden = new Hidden(firstId + "Hidden", null);
		Hidden secondSelectIdHidden = new Hidden(secondId + "Hidden",
				null);

		String hiddenIdStr = firstSelectIdHidden.toHtml()
				+ secondSelectIdHidden.toHtml();

		Select select = new Select(firstId, firstName);
		select.change(firstSelectChanged());
		if (isTrue(firstNullable)) {
			select.emptyOption(firstEmptyOption);
		}
		select.selected(firstValue).style(firstStyle).className(firstClassName);
		if (isFalse(async)) { // 同步加载第一个下拉框数据
			addFirstSelectOptions();
			addOptionsToFirstSelect(select);
		}
		if (isTrue(wrappedInTd)) {
			return hiddenIdStr + buildFirstSelectLable()
					+ new TableCell().add(select).toHtml();
		}
		return hiddenIdStr + buildFirstSelectLable() + select.toHtml();
	}

	/**
	 * 第一个下拉框onchange触发的事件
	 * 
	 * @return js方法
	 */
	protected abstract String firstSelectChanged();

	private String buildFirstSelectLable() {
		if (StringUtils.isBlank(firstLabel)) {
			return StringUtils.EMPTY;
		}
		Label label = new Label(firstLabel);
		if (isTrue(wrappedInTd)) {
			return new TableCell().attr(new Attribute().attr("align", "right"))
					.add(label).toHtml();
		}
		return label.toHtml();
	}

	/**
	 * 异步加载第一个下拉框的数据.
	 */
	protected String asyncLoadFirstSelectOptionsFunction() {
		String function = "(function () {" + "var url="
				+ asyncLoadFirstSelectOptionsUrl() + ";"
				+ "$.post(url, '', function(options) {" + "$('#"
				+ getFirstId() + "').html(options);"
				+ firstSelectChanged() + ";});" + "})();";
		return function;
	}

	/**
	 * 异步加载第一个下拉框的数据的url. <br>
	 * 要求返回一段select的option html代码流.
	 */
	protected String asyncLoadFirstSelectOptionsUrl() {
		return StringUtils.EMPTY;
	}

	/**
	 * 同步加载第一个下拉框的数据.
	 * 
	 * <pre>
	 * 例子:
	 * <br>
	 * protected void addFirstSelectOptions() {
	 *  List options = ...;
	 *   for (Object option : options) {
	 *    addFirstOption(key, value);
	 *   }
	 * }
	 * </pre>
	 */
	protected abstract void addFirstSelectOptions();

	protected void addFirstOption(String value, String text) {
		firstOptions.put(value, text);
	}

	private void addOptionsToFirstSelect(Select select) {
		if (MapUtils.isEmpty(firstOptions)) {
			return;
		}
		for (Map.Entry<String, String> option : firstOptions.entrySet()) {
			select.option(option.getKey(), option.getValue());
		}
	}

	protected boolean isFalse(String bool) {
		return !isTrue(bool);
	}

	protected boolean isTrue(String bool) {
		return BooleanUtils.toBoolean(bool);
	}

	protected String initSecondSelectHtml() {
		Select select = new Select(secondId, secondName);
		if (isTrue(secondNullable)) {
			select.emptyOption(secondEmptyOption);
		}
		select.selected(secondValue).style(secondStyle).className(secondClassName);
		addSecondSelectOptions();
		addOptionsToSecondSelect(select);
		if (isTrue(wrappedInTd)) {
			return buildSecondSelectLable()
					+ new TableCell().add(select).toHtml();
		}
		return buildSecondSelectLable() + select.toHtml();
	}

	private String buildSecondSelectLable() {
		if (StringUtils.isBlank(secondLabel)) {
			return StringUtils.EMPTY;
		}
		Label label = new Label(secondLabel);
		if (isTrue(wrappedInTd)) {
			return new TableCell().attr(new Attribute().attr("align", "right"))
					.add(label).toHtml();
		}
		return label.toHtml();
	}

	protected void addSecondSelectOptions() {

	}

	protected void addSecondOption(String value, String text) {
		secondOptions.put(value, text);
	}

	private void addOptionsToSecondSelect(Select select) {
		if (MapUtils.isEmpty(secondOptions)) {
			return;
		}
		for (Map.Entry<String, String> option : secondOptions.entrySet()) {
			select.option(option.getKey(), option.getValue());
		}
	}

	// ---------------------------------------------------------
	public void setFirstLabel(String firstLabel) {
		this.firstLabel = firstLabel;
	}

	public void setFirstId(String firstId) {
		this.firstId = firstId;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public void setFirstNullable(String firstNullable) {
		this.firstNullable = firstNullable;
	}

	public void setFirstEmptyOption(String firstEmptyOption) {
		this.firstEmptyOption = firstEmptyOption;
	}

	public void setFirstValue(String firstValue) {
		this.firstValue = firstValue;
	}

	public void setSecondLabel(String secondLabel) {
		this.secondLabel = secondLabel;
	}

	public void setSecondId(String secondId) {
		this.secondId = secondId;
	}

	public void setSecondName(String secondName) {
		this.secondName = secondName;
	}

	public void setSecondNullable(String secondNullable) {
		this.secondNullable = secondNullable;
	}

	public void setSecondEmptyOption(String secondEmptyOption) {
		this.secondEmptyOption = secondEmptyOption;
	}

	public String getFirstId() {
		return firstId;
	}

	public String getSecondId() {
		return secondId;
	}

	public boolean isSecondNullable() {
		return isTrue(secondNullable);
	}

	public void setSecondValue(String secondValue) {
		this.secondValue = secondValue;
	}

	public String getSecondValue() {
		return secondValue;
	}

	public void setWrappedInTd(String wrappedInTd) {
		this.wrappedInTd = wrappedInTd;
	}

	public void setFirstStyle(String firstStyle) {
		this.firstStyle = firstStyle;
	}

	public void setSecondStyle(String secondStyle) {
		this.secondStyle = secondStyle;
	}

	public void setAsync(String async) {
		this.async = async;
	}

	public String getFirstValue() {
		return firstValue;
	}

	public void setFirstClassName(String firstClassName) {
		this.firstClassName = firstClassName;
	}

	public void setSecondClassName(String secondClassName) {
		this.secondClassName = secondClassName;
	}

	public String getFirstHasNullValue() {
		return firstNullable;
	}
}
